#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>

#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/uaccess.h>

#include <linux/delay.h>
#include <linux/errno.h>
#include <linux/gpio.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_gpio.h>
#include <asm/io.h>
#include <linux/device.h>
#include <linux/irq.h>
#include <linux/of_irq.h>
#include <linux/platform_device.h>

#define CharDev_name "led_dev"
#define CharDevClass_name "led_class"
#define CharDrv_name    "ChenGreenLed"

static int dev_gpio;
static dev_t dev_no;//dev_t 设备号 32 位，其中，高12位表示主设备号，低20位表示次设备号
static struct cdev chardev_str; //字符设备结构体
static struct class *cdev_class;
static struct device* cdev_device;





//"chengreen_leds"
static int cdev_open(struct inode* inode, struct file* file)
{
    printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
    return 0;
}

static int cdev_close(struct inode* inode, struct file* file)
{
    printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);;
    return 0;
}

static ssize_t cdev_read(struct file* file, char __user* buff, size_t count, loff_t* offp)
{
    char value;
    int ret;
    printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);;
    value = gpio_get_value(dev_gpio);
    ret = copy_to_user(buff, &value, count);
    printk(" the led_gpio value is %d! \n", value);
    return ret;
}

static ssize_t cdev_write(struct file* file, const char __user* buff, size_t count, loff_t* offp)
{
    int retvalue;
    unsigned char databuf[1];
    unsigned char ledstat;
    printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);;
    
    retvalue = copy_from_user(databuf, buff, count);
    ledstat = databuf[0];
    if (ledstat == 0){
        gpio_set_value(dev_gpio, 0);
        printk(" the led is off now! \n");
    }else{
        gpio_set_value(dev_gpio, 1);
        printk(" the led is on now! \n");
    }
    return 0;
}



static struct file_operations cdev_fops = {
    .owner = THIS_MODULE,
    .open = cdev_open,
    .release = cdev_close,
    .write = cdev_write,
    .read = cdev_read,
};


static int CharDev_probe(struct platform_device* pdev)
{
    // 获得设备gpio
    dev_gpio = of_get_named_gpio(pdev->dev.of_node, "gpios", 0);
    if (dev_gpio < 0){
        printk("can not get dev_gpio! \n");
    }
    printk("the dev_gpio is %d\r\n", dev_gpio);
    gpio_request(dev_gpio, "dev_gpio");
    gpio_direction_output(dev_gpio,0);

    //注册一个设备
    alloc_chrdev_region(&dev_no, 0, 1, CharDev_name);//0由kernel分配；1创建一个设备
    cdev_init(&chardev_str,&cdev_fops);
    chardev_str.owner = THIS_MODULE;
    cdev_add(&chardev_str, dev_no, 1);
    //以上三句话用一句表示：
    //major = register_chrdev(0, CharDev_name, &cdev_fops);//0由kernel分配

    cdev_class = class_create(THIS_MODULE, CharDevClass_name);
    cdev_device = device_create(cdev_class, NULL, dev_no, NULL, CharDev_name);

    printk("init finished! \n");
    return 0;
}


static int CharDev_remove(struct platform_device* dev)
{
    cdev_del(&chardev_str);
    unregister_chrdev_region(dev_no,1);
    device_destroy(cdev_class, dev_no);
    class_destroy(cdev_class);
    gpio_free(dev_gpio);
    printk("exit finished! \n");
    return 0;
}


static const struct of_device_id CharDev_compatible[] = {
	{
	    .compatible = "chengreen_leds",
	},
	{},
};





static struct platform_driver driver = {
    .probe = CharDev_probe,
    .remove = CharDev_remove,
    .driver = {
        .name = CharDrv_name,
        .owner = THIS_MODULE,
        .of_match_table = CharDev_compatible,
    },
};


static int __init char_dev_init(void)
{
    printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
    platform_driver_register(&driver);  //注册一个平台驱动
    return 0;

}

static void __exit char_dev_exit(void)
{
    printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
    platform_driver_unregister(&driver);
}

module_init(char_dev_init);
module_exit(char_dev_exit);

MODULE_LICENSE("GPL");